package org.raymond.iworks.core.utils;

import cn.hutool.core.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.raymond.iworks.commons.utils.ErrorUtil;
import org.raymond.iworks.core.security.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class PermissionUtil {
    private static final String PERMISSION_CLASS_SUFFIX="Api.class";

    private PermissionUtil(){};

    public static List<Class> getAllClass(String packageName){
        Set<String> pathSet = ClassUtil.getClassPaths(packageName);
        List<Class> classes = null;
        if(pathSet!=null && pathSet.size()>0){
            List<String> clazzList = new ArrayList<>();
            pathSet.stream().forEach(path->{
                try {
                    Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<>(){
                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                            String filePath = file.toFile().getPath();
                            if (filePath.endsWith(PERMISSION_CLASS_SUFFIX)) {
                                filePath = filePath.substring(filePath.indexOf(File.separator+"classes") + 9, filePath.lastIndexOf("."));
                                filePath = filePath.replace(File.separator, ".").replace("/",".").replace("\\",".");
                                clazzList.add(filePath);
                            }
                            return super.visitFile(file, attrs);
                        }
                    });
                }catch(Exception e){
                    log.error("{}", ErrorUtil.getErrorLog(e));
                };
            });

            classes = clazzList.stream().map(name->{
                Class claz = null;
                try {
                    claz = Class.forName(name);
                }catch (Exception e){
                }
                return claz;
            }).collect(Collectors.toList());
        }
        return classes;
    }

    public static List<Map<String, Object>> groupMethods(Class clz){
        List<Map<String, Object>> mapList = new ArrayList<>();
        if(clz.isAnnotationPresent(RequestMapping.class) && clz.isAnnotationPresent(Resource.class)){
            Map<String, Object> clazObj = new HashMap<>();
            RequestMapping clzRequestMapping = (RequestMapping) clz.getAnnotation(RequestMapping.class);
            String[] clazValues = clzRequestMapping.value();
            String endpoints = StringUtils.joinWith(",", (Object[])clazValues);
            Resource clazRes = (Resource) clz.getAnnotation(Resource.class);
            String resName = clazRes.name();
            clazObj.put("name", resName);
            clazObj.put("uri", endpoints);
            clazObj.put("type", 0);
            clazObj.put("parentId", 0);
            mapList.add(clazObj);
            Method[] methods = clz.getMethods();
            List<String> notSupports = new ArrayList<>();
            Arrays.stream(methods).forEach(m->{
                if(!m.isAnnotationPresent(Resource.class)) return;

                Map<String, Object> methodObj = new HashMap<>();
                methodObj.put("type", 1);

                String mEndpoints = null;
                String mName = null;
                if(m.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping mapping = (RequestMapping) m.getAnnotation(RequestMapping.class);
                    String[] values = mapping.value();
                    String[] paths = mapping.path();
                    mEndpoints = Stream.of(values, paths).flatMap(Arrays::stream).collect(Collectors.joining(","));

                    Resource mRes = (Resource) m.getAnnotation(Resource.class);
                    mName = mRes.name();
                }else if(m.isAnnotationPresent(GetMapping.class)){
                    GetMapping mapping = (GetMapping) m.getAnnotation(GetMapping.class);
                    String[] values = mapping.value();
                    String[] paths = mapping.path();
                    mEndpoints = Stream.of(values, paths).flatMap(Arrays::stream).collect(Collectors.joining(","));

                    Resource mRes = (Resource) m.getAnnotation(Resource.class);
                    mName = mRes.name();
                }else if(m.isAnnotationPresent(PostMapping.class)){
                    PostMapping mapping = (PostMapping) m.getAnnotation(PostMapping.class);
                    String[] values = mapping.value();
                    String[] paths = mapping.path();
                    mEndpoints = Stream.of(values, paths).flatMap(Arrays::stream).collect(Collectors.joining(","));

                    Resource mRes = (Resource) m.getAnnotation(Resource.class);
                    mName = mRes.name();
                }else{
                    notSupports.add(m.getName());
                }
                methodObj.put("name", mName);
                methodObj.put("uri", endpoints+mEndpoints);
                mapList.add(methodObj);
            });
        }
        return mapList;
    }

}
